/**
 * lisenses
 *
 */
package com.openjawtech.xscraper.flights.service.impl;

import com.openjawtech.xscraper.flights.PersistService;
import com.openjawtech.xscraper.flights.dao.AirportDAO;
import com.openjawtech.xscraper.flights.dao.FlightDAO;
import com.openjawtech.xscraper.flights.dao.DAOFactory;
import com.openjawtech.xscraper.flights.domain.Airport;
import com.openjawtech.xscraper.flights.domain.Flight;
import com.openjawtech.xscraper.schema.FlightsType;
import com.openjawtech.xscraper.schema.FlightType;
import com.openjawtech.xscraper.schema.FlightsDocument;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.ArrayList;
import java.util.Date;
import java.net.URL;
import javax.xml.stream.XMLStreamReader;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.Log;
import org.apache.xmlbeans.XmlException;

/**
 * <p>implement all the method of persist service
 *
 * <p>18/02/2009
 * @author Wenbo.Sun
 */
public class PersistServiceImpl implements PersistService {

    private AirportDAO airportDAO;
    private FlightDAO flightDAO;
    private static final Log log = LogFactory.getLog(PersistServiceImpl.class);

    public PersistServiceImpl() {
        log.debug("create dao instances");
        airportDAO = DAOFactory.createAirportDAO();
        flightDAO = DAOFactory.createFlightDAO();
        log.debug("daos have been created");
    }

    @Override
    public void persist(File file) {
        log.debug("try to unmarshal the file");
        try {
            FlightsDocument doc = FlightsDocument.Factory.parse(file);
            FlightsType flights = doc.getFlights();
            log.debug("the file has been unmarshaled");
            formatFlights(flights);
        } catch (XmlException ex) {
            log.error("unmarshal file failed", ex);
        } catch (IOException ex) {
            log.error("unmarshal file failed", ex);
        }
    }

    @Override
    public void persist(URL url) {
        log.debug("try to unmarshal the file");
        try {
            FlightsDocument doc = FlightsDocument.Factory.parse(url);
            FlightsType flights = doc.getFlights();
            log.debug("the file has been unmarshaled");
            formatFlights(flights);
        } catch (XmlException ex) {
            log.error("unmarshal by url failed", ex);
        } catch (IOException ex) {
            log.error("unmarshal by url failed", ex);
        }
    }

    @Override
    public void persist(InputStream stream) {
        log.debug("try to unmarshal the InputStream");
        try {
            FlightsDocument doc = FlightsDocument.Factory.parse(stream);
            FlightsType flights = doc.getFlights();
            log.debug("the InputStream has been unmarshaled");
            formatFlights(flights);
        } catch (XmlException ex) {
            log.error("unmarshal InputStream failed", ex);
        } catch (IOException ex) {
            log.error("unmarshal Inputstream failed", ex);
        }
    }

    @Override
    public void persist(XMLStreamReader reader) {
        log.debug("try to unmarshal the XMLStreamReader");
        try {
            FlightsDocument doc = FlightsDocument.Factory.parse(reader);
            FlightsType flights = doc.getFlights();
            log.debug("the XMLStreamReader has been unmarshaled");
            formatFlights(flights);
        } catch (XmlException ex) {
            log.error("unmarshal XMLStreamReader failed", ex);
        }
    }

    @Override
    public void persist(FlightsType flights) {
        formatFlights(flights);
    }

    @Override
    public boolean removeAirport(String name) {
        if (airportDAO.isExisted(name)) {
            airportDAO.delete(airportDAO.getByName(name));
            return true;
        } else {
            log.error("the airport -- name: " + name + " not existed");
            return false;
        }
    }

    @Override
    public boolean removeFlights(FlightsType flights) {
        log.debug("start to remove flight information");
        String departureName = flights.getDeparture().trim().toLowerCase();
        String arrivalName = flights.getArrival().trim().toLowerCase();
        if (departureName == null || arrivalName == null || "".equals(departureName) || "".equals(arrivalName)) {
            log.error("departure or/and arrival name is/are invalid");
            return false;
        }
        Airport departure = airportDAO.getByName(departureName);
        Airport arrival = airportDAO.getByName(arrivalName);
        if (removeExecute(departure, arrival, flights.getFlightArray())) {
            log.debug("flights information has been removed");
            return true;
        }
        log.error("remove flights information failed");
        return false;
    }

    private boolean removeExecute(Airport departure, Airport arrival, FlightType[] array) {
        for (FlightType flightType : array) {
            if (flightType.getTravelDate() == null) {
                continue;
            }
            Date travelDate = flightType.getTravelDate().getTime();
            Flight flight = flightDAO.getByTravel(departure, arrival, travelDate);
            flightDAO.delete(flight);
        }
        return true;
    }

    @Override
    public boolean updateAirportName(String oldName, String newName) {
        log.debug("change the old airport name -- " + oldName + " to -- " + newName);
        if (!airportDAO.isExisted(oldName)) {
            log.error("the airport -- name: " + oldName + " not existed");
            return false;
        } else if (airportDAO.isExisted(newName)) {
            log.error("the new name: " + newName + " has been existed");
            return false;
        } else {
            Airport airport = airportDAO.getByName(newName);
            airport.setAirportName(newName);
            log.debug("airport name has been changed");
            return true;
        }

    }

    private void formatFlights(FlightsType flights) {
        log.debug("clean and validate the flight information");
        String departureName = flights.getDeparture().trim().toLowerCase();
        String arrivalName = flights.getArrival().trim().toLowerCase();
        if (departureName == null || arrivalName == null || "".equals(departureName) || "".equals(arrivalName)) {
            log.error("departure or/and arrival name is/are invalid");
            return;
        }
        Airport departure = new Airport(departureName);
        Airport arrival = new Airport(arrivalName);
        List<Flight> list = new ArrayList<Flight>();
        for (FlightType flightType : flights.getFlightArray()) {
            if (flightType.getTravelDate() == null || flightType.getPrice() == null) {
                continue;
            }
            Date travelDate = flightType.getTravelDate().getTime();
            double price = flightType.getPrice().doubleValue();
            Flight flight = new Flight(travelDate, price);
            list.add(flight);
        }
        persistExecute(departure, arrival, list);
    }

    private void persistExecute(Airport departure, Airport arrival, List<Flight> list) {
        log.debug("start to persist all the flight information");
        if (!airportDAO.isExisted(departure.getAirportName())) {
            airportDAO.save(departure);
        }
        if (!airportDAO.isExisted(arrival.getAirportName())) {
            airportDAO.save(arrival);
        }
        departure = airportDAO.getByName(departure.getAirportName());
        arrival = airportDAO.getByName(arrival.getAirportName());
        for (Flight flight : list) {
            flight.setAirportByDepartureId(departure);
            flight.setAirportByArrivalId(arrival);
            if (flightDAO.isExisted(departure, arrival, flight.getTravelDate())) {
                double price = flight.getPrice();
                flight = flightDAO.getByTravel(departure, arrival, flight.getTravelDate());
                flight.setPrice(price);
                flightDAO.update(flight);
            } else {
                flightDAO.save(flight);
            }
        }
        log.debug("all inforamtion has been persisted");
    }
}
